//************************************************************************
//Developed by Rudolf Henning 2002/2003 (VB.Net) 2004 (C#)
//Source code:
//  The source code may be freely modified or used but this credit section may not be removed 
//  The original author must be notified of any modifications plus a copy of 
//  the changed source code
//Contact info: mailto://Rudolf@Hen.co.za 
//
//Changed by Guo Ming Li 2009/2010 (Contact info: mailto://gming.li@gmail.com)
//************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using GML.BizTalk.OperationManager.MSBTSOperations;

namespace GML.BizTalk.OperationManager.Library
{
    public class GACList
    {
        #region Privates
        private readonly string GACDirectory = "\\c$\\Windows\\assembly\\GAC\\"; //default for .Net 1.x
        private readonly string GAC_32Directory = "\\c$\\Windows\\assembly\\GAC_32\\";
        private readonly string GAC_64Directory = "\\c$\\Windows\\assembly\\GAC_64\\";
        private readonly string GAC_MSILDirectory = "\\c$\\Windows\\assembly\\GAC_MSIL\\"; //default for .Net 2.x
        private GACVerificationFilter verificationfilter;
        
        #endregion

        #region Constructor
        public GACList(GACVerificationFilter _filter)
        {
            this.verificationfilter = _filter;
        }

        public GACList()
        {
            this.verificationfilter = new GACVerificationFilter();
        }

        #endregion

        #region Properties
        private List<GACEntry> assemblies = new List<GACEntry>();
        public List<GACEntry> Assemblies
        {
            get { return assemblies; }
            set { assemblies = value; }
        }

        private List<string> assembliesToString = new List<string>();
        public List<string> AssembliesToString
        {
            get { return assembliesToString; }
            set { assembliesToString = value; }
        }

        private string machineName;
        public string MachineName
        {
            get { return machineName; }
            set { machineName = value; }
        }
        #endregion

        #region Methods
        public void LoadList(string machineName, string filter)
        {
            MachineName = machineName;
            if (filter.Length == 0)
                filter = "*";
            assemblies = new List<GACEntry>();
            GetGACFiles("\\\\" + machineName + GACDirectory, filter);
            GetGACFiles("\\\\" + machineName + GAC_MSILDirectory, filter);
            GetGACFiles("\\\\" + machineName + GAC_32Directory, filter);
            GetGACFiles("\\\\" + machineName + GAC_64Directory, filter);
        }
        private void GetGACFiles(string rootDirectory, string filter)
        {
            string[] assemblyDirectories = null;
            string[] versionTokenDirectories = null;
            string[] assemblyFiles = null;
            GACEntry gacEntry = null;

            try
            {
                if (Directory.Exists(rootDirectory))
                {
                    assemblyDirectories = Directory.GetDirectories(rootDirectory, filter);
                    foreach (string assemblyDirectory in assemblyDirectories)
                    {
                        versionTokenDirectories = Directory.GetDirectories(assemblyDirectory);
                        foreach (string versionTokenDirectory in versionTokenDirectories)
                        {
                            assemblyFiles = Directory.GetFiles(versionTokenDirectory, "*.*"); ///There should only be one... Don't loose your head

                            gacEntry = new GACEntry();
                            gacEntry.MachineName = MachineName;
                            gacEntry.AssemblyName = Path.GetFileName(assemblyDirectory);
                            gacEntry.AssemblyVersion = GetVersionFromVersionToken(versionTokenDirectory).ToString();
                            gacEntry.AssemblyCulture = GetCultureFromVersionToken(versionTokenDirectory);
                            gacEntry.AssemblyPublicKeyToken = GetTokenFromVersionToken(versionTokenDirectory);
                            gacEntry.AssemblyPaths = assemblyFiles;
                            foreach (string assemblyDll in assemblyFiles)
                            {
                                if (assemblyDll.EndsWith(".dll") || assemblyDll.EndsWith(".exe"))
                                {
                                    FileInfo fi = new FileInfo(assemblyDll);
                                    gacEntry.AssemblyFileSize = fi.Length;
                                    gacEntry.CreateDate = fi.CreationTime; //date installed in GAC
                                    gacEntry.CreateDateSpecified = true;

                                    System.Diagnostics.FileVersionInfo fileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(assemblyDll);
                                    gacEntry.AssemblyFileSize = fi.Length;
                                    gacEntry.AssemblyFileSizeSpecified = true;
                                    gacEntry.AssemblyFileVersion = fileVersionInfo.FileVersion;
                                    gacEntry.AssemblyProductVersion = fileVersionInfo.ProductVersion;

                                }
                            }

                            if (!assemblies.Contains(gacEntry))//avoid duplicates 
                            {
                                assemblies.Add(gacEntry);
                                assembliesToString.Add(gacEntry.ToLongString(verificationfilter));
                            }
                        }
                    }
                }

            }
            catch (Exception ex) { throw ex; }
            finally
            {
                assemblyDirectories = null;
                versionTokenDirectories = null;
                assemblyFiles = null;
                gacEntry = null;
            }
        }
        private string GetTokenFromVersionToken(string versionToken)
        {
            string tempName = null;

            try
            {
                tempName = System.IO.Path.GetFileName(versionToken); 
                tempName = tempName.Substring(tempName.LastIndexOf("_") + 1);
                return tempName;

            }
            catch (Exception ex) { throw ex; }
            finally
            {
                tempName = null;
            }
        }
        private string GetCultureFromVersionToken(string versionToken)
        {
            string tempName = null; 

            try
            {
                tempName = System.IO.Path.GetFileName(versionToken);
                tempName = tempName.Substring(tempName.IndexOf("_") + 1);
                if (tempName.StartsWith("_"))
                    tempName = "neutral";
                else
                    tempName = tempName.Substring(0, tempName.IndexOf("_"));
                return tempName;
            }
            catch (Exception ex) { throw ex; }
            finally
            {
                tempName = null;
            }
        }
        private Version GetVersionFromVersionToken(string versionToken)
        {
            string tempName = null;
            Version v = null;
            try
            {
                tempName = System.IO.Path.GetFileName(versionToken);
                tempName = tempName.Substring(0, tempName.IndexOf("_"));
                v = new Version(tempName);
                return v;
            }
            catch (Exception ex) { throw ex; }
            finally
            {
                tempName = null;
                v = null;
            }
        }

        public List<GACEntry> GetUnique(List<GACEntry> compareTo)
        {
            bool found = false;
            List<GACEntry> unique = new List<GACEntry>();
            try
            {
                foreach (GACEntry gacEntry in assemblies)
                {
                    found = false;
                    foreach (GACEntry compareEntry in compareTo)
                    {
                        if (gacEntry.Equals(compareEntry))
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                        unique.Add(gacEntry);
                }
                return unique;

            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                unique = null;
            }
        }

        //TODO: implement this GetMissingEntry method
        public List<GACEntry> GetMissingEntry(GACEntry[] compareTo, GACVerificationFilter comparisonFilter)
        {
            bool found = false;
            List<GACEntry> missing = new List<GACEntry>();
            try
            {
                foreach (GACEntry compareEntry in compareTo)
                {
                    found = false;
                    foreach (GACEntry gacEntry in assemblies)
                    {
                        if (gacEntry.Equals(compareEntry, comparisonFilter))
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                        missing.Add(compareEntry);
                }
                return missing;

            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                missing = null;
            }
        }
        public List<GACEntry> GetUniqueVersion(List<GACEntry> compareTo)
        {
            bool found = false;
            bool foundVersion = false;
            List<GACEntry> unique = new List<GACEntry>();
            try
            {
                foreach (GACEntry gacEntry in assemblies)
                {
                    found = false;
                    foundVersion = false;
                    foreach (GACEntry compareEntry in compareTo)
                    {
                        if (gacEntry.AssemblyName == compareEntry.AssemblyName)
                        {
                            found = true;
                            if (gacEntry.AssemblyVersion.Equals(compareEntry.AssemblyVersion))
                            {
                                foundVersion = true;
                                break;
                            }
                        }
                    }

                    if (found && !foundVersion)
                        unique.Add(gacEntry);
                }
                return unique;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                unique = null;
            }
        } 
        #endregion
    }
}
